#import "SMROverlayShadowEffectView.h"

static const CGFloat SMROverlayShadowEffectViewShadowOffset = 2.0f;
static const CGFloat SMROverlayShadowEffectViewShadowBlur = 5.0f;
static const CGFloat SMROverlayShadowEffectViewFramePadding = 2.0f;
static const CGFloat SMROverlayShadowEffectViewCornerRadius = 5.0f;
static NSShadow *SMROverlayShadowEffectViewShadow;
static NSColor *SMROverlayShadowEffectViewFrameColor;
static NSColor *SMROverlayShadowEffectViewFrameColorDisabled;


#pragma mark -
@interface SMROverlayShadowEffectView ()
@property (nonatomic, retain) NSBezierPath *clippingPath;
@property (nonatomic, retain) NSBezierPath *framePath;
@end


#pragma mark -
@interface SMROverlayShadowEffectView (SMROverlayShadowEffectViewPrivateMethods)
-(void)initializeView_;
-(void)refreshView_;
//-(void)updateOverlayRect:(NSRect)visibleRect;
@end


#pragma mark -
@implementation SMROverlayShadowEffectView
@synthesize clippingPath;
@synthesize framePath;
@synthesize targetView;
@synthesize borderColor;
@synthesize inactiveBorderColor;
@synthesize backgroundColor;
@synthesize inactiveBackgroundColor;
@synthesize dropShadow;
@synthesize inactiveDropShadow;
@synthesize borderPadding;


+(void)initialize {
	SMROverlayShadowEffectViewShadow = [[NSShadow alloc] init];
	[SMROverlayShadowEffectViewShadow setShadowColor:[NSColor colorWithCalibratedWhite:0.3 alpha:0.5]];
	[SMROverlayShadowEffectViewShadow setShadowOffset:NSMakeSize(0, -SMROverlayShadowEffectViewShadowOffset)];
	[SMROverlayShadowEffectViewShadow setShadowBlurRadius:SMROverlayShadowEffectViewShadowBlur];
	SMROverlayShadowEffectViewFrameColor = [[NSColor colorWithCalibratedRed:0.473 green:0.526 blue:0.606 alpha:1.000] copy];
	SMROverlayShadowEffectViewFrameColorDisabled = [[NSColor lightGrayColor] copy];
}

-(id)initWithFrame:(NSRect)frameRect {
	if (!(self = [super initWithFrame:frameRect]))
		return nil;
	[self initializeView_];
	return self;
}

-(id)initWithCoder:(NSCoder *)aDecoder {
	if (!(self = [super initWithCoder:aDecoder]))
		return nil;
	[self initializeView_];
	return self;
}

-(void)initializeView_ {
	[self setBorderColor:SMROverlayShadowEffectViewFrameColor];
	[self setBackgroundColor:[NSColor whiteColor]];
	[self setDropShadow:SMROverlayShadowEffectViewShadow];
	[self setInactiveBorderColor:SMROverlayShadowEffectViewFrameColorDisabled];
	[self setInactiveBackgroundColor:[NSColor whiteColor]];
	[self setInactiveDropShadow:SMROverlayShadowEffectViewShadow];
	[self setBorderPadding:SMROverlayShadowEffectViewFramePadding];
	NSNotificationCenter *noteCenter = [NSNotificationCenter defaultCenter];
	[noteCenter addObserver:self selector:@selector(refreshView_) name:NSWindowDidResignKeyNotification object:nil];
	[noteCenter addObserver:self selector:@selector(refreshView_) name:NSWindowDidBecomeKeyNotification object:nil];
	[self setAutoresizingMask:NSViewNotSizable];
}

-(void)setOverlayRect:(NSRect)visibleRect inView:(NSView *)newTargetView {
	if ([self superview])
		[self removeFromSuperview];
	[self setTargetView:newTargetView];
	BOOL targetIsFlipped = [newTargetView isFlipped];
	// frameRect is the rectangle that will get a fill and stroke in drawRect
	NSRect frameRect = NSInsetRect(visibleRect, -[self borderPadding], -[self borderPadding]);
	frameRect = NSIntegralRect(frameRect);
	// overlayRect is the entire region drawn by the receiver including the frame and the shadow
	NSRect overlayRect = NSOffsetRect(frameRect, 0.0f, SMROverlayShadowEffectViewShadowOffset * (targetIsFlipped ? 1 : -1));
	overlayRect = NSInsetRect(overlayRect, -SMROverlayShadowEffectViewShadowBlur, -SMROverlayShadowEffectViewShadowBlur);
	NSRect overlayRectSuperviewCoordinates = NSZeroRect;
	NSRect superviewRect = NSZeroRect;
	NSView *currentView = nil;
	NSView *currentSuperview = newTargetView;
	NSScrollView *enclosingScrollView = [newTargetView enclosingScrollView];
	do {
		currentView = currentSuperview;
		// If the target is inside a scroll view and we've worked up the view hierarchy to the scroll view's document view, stop there even though the overlay view will be clipped.  This is expected behavior
		if (enclosingScrollView && currentView == [enclosingScrollView documentView])
			break;
		// If we've reached the window's content view, stop there and attach to it even though the overlay view will be clipped.  
		if (currentView == [[currentView window] contentView]) 
			break;
		currentSuperview = [currentView superview];
		overlayRectSuperviewCoordinates = [currentSuperview convertRect:overlayRect fromView:newTargetView];
		superviewRect = [currentSuperview bounds];
	} while (!NSContainsRect(superviewRect, overlayRectSuperviewCoordinates));
	[currentSuperview addSubview:self positioned:NSWindowAbove relativeTo:nil];
	[self setNextResponder:currentSuperview];

	[self setFrame:overlayRectSuperviewCoordinates];
	// Cache the path for drawing the stroke around the target view
	NSRect myFrame = [self convertRect:frameRect fromView:newTargetView];
	myFrame = NSInsetRect(myFrame, 0.5, 0.5);
	[self setFramePath:[NSBezierPath bezierPathWithRoundedRect:myFrame xRadius:SMROverlayShadowEffectViewCornerRadius yRadius:SMROverlayShadowEffectViewCornerRadius]];
	
	// Create a clipping path that will leave the visibleRect of the targetView untouched in drawRect
	NSRect myBounds = NSMakeRect(0.0f, 0.0f, overlayRect.size.width, overlayRect.size.height);
	NSRect clipRect = [self convertRect:visibleRect fromView:newTargetView];
	NSBezierPath *clipPath = [NSBezierPath bezierPathWithRect:myBounds];
	[clipPath setWindingRule:NSEvenOddWindingRule];
	[clipPath appendBezierPathWithRect:clipRect];
	[self setClippingPath:clipPath];
	[self setNeedsDisplay:YES];
}

-(void)updateOverlayRect:(NSRect)visibleRect {
	// frameRect is the rectangle that will get a fill and stroke in drawRect
	NSRect frameRect = NSInsetRect(visibleRect, -[self borderPadding], -[self borderPadding]);
	BOOL targetIsFlipped = [[self targetView] isFlipped];
	NSRect overlayRect = NSOffsetRect(frameRect, 0.0f, SMROverlayShadowEffectViewShadowOffset * (targetIsFlipped ? 1 : -1));
	overlayRect = NSInsetRect(overlayRect, -SMROverlayShadowEffectViewShadowBlur, -SMROverlayShadowEffectViewShadowBlur);
	NSRect overlayRectSuperviewCoordinates = [[self superview] convertRect:overlayRect fromView:[self targetView]];
	[self setFrame:overlayRectSuperviewCoordinates];
	NSRect myFrame = [self convertRect:frameRect fromView:[self targetView]];
	myFrame = NSInsetRect(myFrame, 0.5, 0.5);
	[self setFramePath:[NSBezierPath bezierPathWithRoundedRect:myFrame xRadius:SMROverlayShadowEffectViewCornerRadius yRadius:SMROverlayShadowEffectViewCornerRadius]];

	// Create a clipping path that will leave the visibleRect of the targetView untouched in drawRect
	NSRect myBounds = NSMakeRect(0.0f, 0.0f, overlayRect.size.width, overlayRect.size.height);
	NSRect clipRect = [self convertRect:visibleRect fromView:[self targetView]];
	NSBezierPath *clipPath = [NSBezierPath bezierPathWithRect:myBounds];
	[clipPath setWindingRule:NSEvenOddWindingRule];
	[clipPath appendBezierPathWithRect:clipRect];
	[self setClippingPath:clipPath];
	[self setNeedsDisplay:YES];
}

-(void)drawRect:(NSRect)dirtyRect {
	[[self clippingPath] addClip];
	NSColor *fillColor = [NSApp isActive] ? [self backgroundColor] : [self inactiveBackgroundColor];
	[fillColor setFill];
	
	[NSGraphicsContext saveGraphicsState];
	NSShadow *shadow = [NSApp isActive] ? [self dropShadow] : [self inactiveDropShadow];
	[shadow set];
	[[self framePath] fill];
	[NSGraphicsContext restoreGraphicsState];
	
	NSColor *frameColor = [NSApp isActive] ? [self borderColor] : [self inactiveBorderColor];
	[frameColor setStroke];
	[[self framePath] stroke];
}

-(void)refreshView_ {
	[self setNeedsDisplay:YES];
}

-(NSView *)hitTest:(NSPoint)aPoint {
	return nil;
}

@end
